﻿using System;
using System.Collections.Generic;
using MemoryPack;
#if UNITY_EDITOR
using Sirenix.OdinInspector;
#endif

namespace Hsenl {
    // 综合节点, 可以放无数个子节点, 是综合类节点的基类
    [Serializable]
    [MemoryPackable(GenerateType.NoGenerate)]
    public abstract partial class CompositeNode<TManager, TNode> : Node<TManager>, IBreadNode<TNode>
        where TManager : class, IBehaviorTree
        where TNode : class, INode {
        [MemoryPackIgnore]
        protected int position;

#if UNITY_EDITOR
        [ShowInInspector]
#endif
        [MemoryPackInclude]
        protected List<INode> children = new();

        [MemoryPackOnSerialized]
        private void OnMemoryPackSerialized() {
            if (this.children == null) return;
            foreach (var child in this.children) {
                SetParent_Internal(child, this);
            }
        }

        public sealed override void AwakeNode(IBehaviorTree tree) {
            base.AwakeNode(tree);
            for (int i = 0, len = this.children.Count; i < len; i++) {
                this.children[i].AwakeNode(tree);
            }
        }

        public sealed override void EnableNode() {
            base.EnableNode();
            for (int i = 0, len = this.children.Count; i < len; i++) {
                this.children[i].EnableNode();
            }
        }

        public sealed override void DisableNode() {
            base.DisableNode();
            for (int i = 0, len = this.children.Count; i < len; i++) {
                this.children[i].DisableNode();
            }
        }

        public sealed override void DestroyNode() {
            base.DestroyNode();
            for (int i = 0, len = this.children.Count; i < len; i++) {
                this.children[i].DestroyNode();
            }
        }

        public sealed override void ResetNode() {
            base.ResetNode();
            for (int i = 0, len = this.children.Count; i < len; i++) {
                this.children[i].ResetNode();
            }
        }

        public sealed override void AbortNode() {
            base.AbortNode();
            for (int i = 0, len = this.children.Count; i < len; i++) {
                this.children[i].AbortNode();
            }

            this.position = 0;
        }

        public sealed override bool EvaluateNode() {
            return base.EvaluateNode();
        }

        public sealed override NodeStatus TickNode() {
            return base.TickNode();
        }

        public sealed override void StartRun() {
            base.StartRun();
        }

        public sealed override void Run() {
            base.Run();
        }

        public sealed override void StopRun() {
            base.StopRun();
        }

        public bool AddChild(TNode node) {
            if (this.manager != null)
                throw new InvalidOperationException("Can't modified nodes when node is awaked.");

            if (node == null)
                throw new ArgumentNullException();

            this.children.Add(node);
            SetParent_Internal(node, this);
            node.ForeachChildren(SetParent_Internal, this);

            return true;
        }

        public bool RemoveChild(TNode node) {
            if (this.manager != null)
                throw new InvalidOperationException("Can't modified nodes when node is awaked.");

            if (node == null)
                throw new ArgumentNullException();

            if (node.Parent != this)
                return false;

            var b = this.children.Remove(node);
            SetParent_Internal(node, null);
            node.ForeachChildren(SetParent_Internal, default(TNode));

            return b;
        }

        public void Clear() {
            if (this.manager != null)
                throw new InvalidOperationException("Can't modified nodes when node is awaked.");

            for (var i = this.children.Count - 1; i >= 0; i--) {
                var child = this.children[i];
                this.children.RemoveAt(i);
                SetParent_Internal(child, null);
                child.ForeachChildren(SetParent_Internal, default(TNode));
            }

            this.children.Clear();
        }

        public sealed override IteratorList<INode> ForeachChildrenExcludeGrandchild() {
            return new IteratorList<INode>(this.children);
        }

        public override void ForeachChildren<T>(Action<INode, T> action, T userData = default) {
            if (this.children == null) return;
            for (int i = 0, len = this.children.Count; i < len; i++) {
                var child = this.children[i];
                action(child, userData);
                child.ForeachChildren(action, userData);
            }
        }

        public sealed override T GetNodeInChildren<T>() {
            if (this.children == null) return default;
            for (int i = 0, len = this.children.Count; i < len; i++) {
                var child = this.children[i];
                if (child is T t) {
                    return t;
                }

                var ret = child.GetNodeInChildren<T>();
                if (ret != null) {
                    return ret;
                }
            }

            return default;
        }

        public sealed override T[] GetNodesInChildren<T>() {
            if (this.children == null)
                return null;

            using var list = ListComponent<T>.Rent();
            this.GetNodesInChildren(list);
            return list.ToArray();
        }

        public sealed override void GetNodesInChildren<T>(List<T> cache) {
            if (this.children == null) return;
            for (int i = 0, len = this.children.Count; i < len; i++) {
                var child = this.children[i];
                if (child is T t) {
                    cache.Add(t);
                }

                child.GetNodesInChildren(cache);
            }
        }
    }
}